using System;
using IFXplus.SmartStorage.Providers;
using IFXplus.SmartStorage.Settings;

namespace IFXplus.SmartStorage
{
    internal static class StorageService
    {
        public static void Insert(StorageItemMetadata metadata, object value)
        {
            try
            {
                CheckMetadata(metadata);

                if (value == null)
                    throw new ArgumentNullException("value");

                // remove from each storage provider:
                Remove(metadata);

                // insert in each storage provider:
                StorageEntry entry = new StorageEntry(metadata.Key, DateTime.Now, metadata.AbsoluteTTL, value);
                foreach (StorageProviderReference spr in metadata.Providers)
                    StorageProviderFactory.GetStorageProvider(spr).Insert(entry);
                
            }
            catch(Exception ex)
            {
                throw new StorageException(string.Format("Exception in StorageService.Insert({0}, {1})", metadata, value), ex);
            }
        }

        public static void Remove(StorageItemMetadata metadata)
        {
            try            
            {
                CheckMetadata(metadata);

                // remove from each storage provider:
                foreach (StorageProviderReference spr in metadata.Providers)
                    StorageProviderFactory.GetStorageProvider(spr).Remove(metadata.Key);
            }
            catch (Exception ex)
            {
                throw new StorageException(string.Format("Exception in StorageService.Remove({0})", metadata), ex);
            }
        }

        public static object Get(StorageItemMetadata metadata)
        {
            try
            {
                CheckMetadata(metadata);
                
                // init return object (null value as default):
                StorageEntry entry;

                // try to find the item in the best storage provider (from 1st to last):
                for (int i = 0; i < metadata.Providers.Count; i++)
                {
                    entry = StorageProviderFactory.GetStorageProvider(metadata.Providers[i]).Get(metadata.Key);
                    if (entry != null)
                    {
                        // check entry expiration (TTL):
                        if (entry.IsExpired(metadata.AbsoluteTTL))
                        {
                            Remove(metadata);
                            return null;
                        }

                        // item found (and not expired) but not in the 1st storage provider:
                        if (i > 0)
                        {
                            // update the default absolute TTL with current value:
                            entry.DefaultAbsoluteTTL = metadata.AbsoluteTTL;

                            // insert the item into the previous storage provider for next calls:
                            StorageProviderFactory.GetStorageProvider(metadata.Providers[i - 1]).Insert(entry);
                        }
                        return entry.Value;
                    }
                }
                
                // item not found, return a null reference:
                return null;

            }
            catch (Exception ex)
            {
                throw new StorageException(string.Format("Exception in StorageService.Get({0})", metadata), ex);
            }
        }

        private static void CheckMetadata(StorageItemMetadata metadata)
        {
            if (metadata == null)
                throw new ArgumentNullException("metadata");
            if (metadata.Providers == null || metadata.Providers.Count < 1)
                throw new ArgumentException("metadata.Providers cannot be null or empty", "metadata");
            if (metadata.Key.Length > StorageSettings.MAX_KEY_LENGTH)
                throw new ArgumentException(string.Format("The length of the metadata.Key exceeds the maximum length allowed ({0})", StorageSettings.MAX_KEY_LENGTH), "metadata");                
        }

    }
}
